
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <strings.h>
#include <stdio.h>
#include "EfficientMap.cc"
#include "TwoWayList.cc"
#include "Types.cc"

class LeftRecord;
class RightRecord;

class RightRecord
{

private:

#include "AttsRight.cc"

    friend bool Matches (LeftRecord & left, RightRecord & right);
    friend void WriteOut (LeftRecord & left, RightRecord & right, FILE *outFile);
public:

    RightRecord  () {}
    ~RightRecord  () {}

    void Swap (RightRecord & withMe)
	{
	    char temp[sizeof (RightRecord)];
	    memmove (temp, this, sizeof (RightRecord));
	    memmove (this, &withMe, sizeof (RightRecord));
	    memmove (&withMe, temp, sizeof (RightRecord));
	}

    bool ReadIn (FILE *fromMe)
	{
	    bool res;
#include "ReadInRight.cc"
	    fgetc_unlocked (fromMe);
	    return res;
	}

    long GetHash ()
	{
	    long hash = 0;
#include "HashRight.cc"
	    return hash;
	}
	
};

class LeftRecord
{

private:

#include "AttsLeft.cc"

    friend bool Matches (LeftRecord & left, RightRecord & right);
    friend void WriteOut (LeftRecord & left, RightRecord & right, FILE *outFile);
	
public:

    LeftRecord  () {}
    ~LeftRecord  () {}

    void Swap (LeftRecord & withMe)
	{
	    char temp[sizeof (LeftRecord)];
	    memmove (temp, this, sizeof (LeftRecord));
	    memmove (this, &withMe, sizeof (LeftRecord));
	    memmove (&withMe, temp, sizeof (LeftRecord));
	}

    bool ReadIn (FILE *fromMe)
	{
	    bool res;
#include "ReadInLeft.cc"
	    fgetc_unlocked(fromMe);
	    return res;
	}

    long GetHash ()
	{
	    long hash = 0;
#include "HashLeft.cc"
	    return hash;
	}

};

bool Matches (LeftRecord & left, RightRecord & right)
{
#include "Predicate.cc"
}

void WriteOut (LeftRecord & left, RightRecord & right, FILE *toMe)
{
#include "WriteOut.cc"
  fputc_unlocked('\n', toMe);
}

class Hash
{

private:

    long value;

public:

    Hash (long inVal)
	{
	    value = inVal;
	}

    ~Hash () {}
    Hash () {}

    void Swap (Hash & withMe)
	{
	    long temp = value;
	    value = withMe.value;
	    withMe.value = temp;
	}

    int IsEqual (Hash & withMe)
	{
	    return withMe.value == value;
	}

    int LessThan (Hash & withMe)
	{
	    return withMe.value < value;
	}
};

//#include "OutputRight.cc"
// Example of what would be in OutputRight.cc
static const bool outputRight = false;

// JES TODO: It appears that nested loop joins use the hasing regardless and
// this may be suboptimal for performance when a nested loop join is requied.
int
main(int argc, char **argv)
{
    if (argc != 4)
    {
	fprintf(stderr, "Usage: AntiJoin leftTableFile rightTableFile outFile\n");
	exit(-1);
    }

    // this is the main lookup table for the join
    EfficientMap <Hash, TwoWayList <LeftRecord> > lookupTable;

    // first, we read in the left input table
    FILE *inFile = fopen (argv[1], "r");

    // now go through the left table and hash everything
    LeftRecord myRec;
    while (myRec.ReadIn(inFile))
    {
	Hash myHash(myRec.GetHash());

	// if that hash value is already there, then just add the record in
	if (lookupTable.IsThere(myHash))
	{
	    TwoWayList<LeftRecord> & values = lookupTable.Find(myHash);
	    values.Insert(myRec);

	    // otherwise, create a new pair and add it in
	}
	else
	{
	    TwoWayList<LeftRecord> values;
	    values.Insert(myRec);
	    lookupTable.Insert(myHash, values);		
	}
    }

    // this is where the output goes
    FILE *outFile = fopen(argv[3], "w");

    // and go through the right table and try to find all of the matches
    fclose(inFile);
    inFile = fopen(argv[2], "r");
    RightRecord lookupRec;

OUTER_WHILE:
    while (lookupRec.ReadIn(inFile))
    {
	Hash myHash(lookupRec.GetHash());

	// if that hash value is there
	if (lookupTable.IsThere(myHash))
	{
	    TwoWayList<LeftRecord> & values = lookupTable.Find(myHash);

	    // join with every potential match
	    for (values.MoveToStart(); values.RightLength() > 0; values.Advance())
	    {
		if (Matches(values.Current(), lookupRec))
		{
		    //WriteOut(values.Current(), lookupRec, outFile);
		    if (outputRight)
		    {
			// If there is a match and outputting the right then don't
			// output this record.
			goto OUTER_WHILE;
		    }
		    else
		    {
			// If the left table is the one we are outputting then remove
			// this record from the look up table.
			LeftRecord tempRecord;
			values.Remove(tempRecord);
			if (!values.AtEnd())
			{
			    continue;
			}
			else if (values.IsEmpty())
			{
			    TwoWayList<LeftRecord> tempList;
			    if (lookupTable.Remove(myHash, myHash, tempList) != 1)
			    {
				// Unexpected error occurred on remove.
				fprintf(stderr, "ERROR: Hash table remove failed when key "
					"was expected to be there.\n");
				exit(-1);
			    }
			}
		    } // end if (outputRight)
		} // end if (Matches(values.Current(), lookupRec))
	    } // end for (values.MoveToStart(); values.RightLength() > 0; values.Advance())
	} // end if (lookupTable.IsThere(myHash))
	else if (outputRight)
	{
	    WriteOut(myRec, lookupRec, outFile);
	}
    }

    fclose (inFile);

    // Write out all of the remaining records in the hash table if outputting the left.
    if (!outputRight)
    {
	RightRecord tempRightRecord;
	for (lookupTable.MoveToStart(); !lookupTable.AtEnd(); lookupTable.Advance())
	{
	    TwoWayList<LeftRecord> & values = lookupTable.CurrentData();
	    for (values.MoveToStart(); values.RightLength() > 0; values.Advance())
	    {
		WriteOut(values.Current(), tempRightRecord, outFile);
	    }
	}
	
    }

    fclose (outFile);

    return 0;
}
